﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Swift;
using Utils;

namespace TB {
    public class ActorFactory : IFrameDrived {

        public static CoroutineManager CM;

        public ActorFactory(Connection[] conn) {
            this.conn = conn;
            Alive = true;
        }

        #region 基础功能，创建、删除、移动、替换
        void CreateActor(Actor a) {
            for(int i = 0; i < conn.Length; i++) {
                IWriteableBuffer buff = conn[i].BeginSend ("Factory");
                buff.Write ("Add");
                buff.Write (a.TID);
                buff.Write (a.UID);
                buff.Write (a.GetX ());
                buff.Write (a.GetY ());
                buff.Write ((int) a.Dir);
                buff.Write (a.Tag);

                conn[i].End (buff);
            }

            a.Alive = true;
            objects.AddLast (a);
        }

        public Actor CreateActor(int tid, int _x, int _y, float size, string tag, DIR _dir = DIR.UP, int blood=0) {
            Actor actor = new Actor {
                TID = tid,
                Tag = tag,
                Blood = blood,
                UID = UniqueID.Create(),
                Size = size,
                X = _x,
                Y = _y,
                Dir = _dir
            };

            CreateActor (actor);

            return actor;
        }

        public Actor CreateActor(int tid, float x, float y, float size, string tag, DIR _dir = DIR.UP, int blood=0) {
            Actor actor = new Actor {
                TID = tid,
                Tag = tag,
                Blood = blood,
                UID = UniqueID.Create (),
                Size = size,
                Dir = _dir
            };

            actor.SetX (x);
            actor.SetY (y);

            CreateActor (actor);

            
            return actor;
        }

        public void RemoveActor(Actor actor) {

            if(actor == null)
                return;

            if(actor.Alive == false)
                return;

            actor.Alive = false;

            if(actor.TID >= 300 && actor.TID < 1000) {
                kill++;
            }

            if(objects.Contains (actor)) {
                objects.Remove (actor);
            }

            if(locomotorObjects.Contains (actor)) {
                locomotorObjects.Remove (actor);
            }

            for(int i = 0; i < conn.Length; i++) {
                Console.WriteLine ("conn[" + i + "] = " + conn[i] + "，IsConnected = " + conn[i].IsConnected);
                IWriteableBuffer buff = conn[i].BeginSend ("Factory");
                buff.Write ("Remove");
                buff.Write (actor.TID);
                buff.Write (actor.UID);
                conn[i].End (buff);
            }
        }

        public void Refresh(Actor actor) {

            for(int i = 0; i < conn.Length; i++) {
                if(conn[i] != null) {
                    IWriteableBuffer buff = conn[i].BeginSend ("Factory");
                    buff.Write ("Refresh");
                    buff.Write (actor.UID);
                    buff.Write (actor.TID);
                    buff.Write (actor.GetX ());
                    buff.Write (actor.GetY ());
                    buff.Write ((int) actor.Dir);
                    conn[i].End (buff);
                }
            }

        }

        public void Repos(Actor actor) {
            for(int i = 0; i < conn.Length; i++) {
                IWriteableBuffer buff = conn[i].BeginSend ("Factory");
                buff.Write ("Repos");
                buff.Write (actor.TID);
                buff.Write (actor.UID);
                buff.Write (actor.GetX ());
                buff.Write (actor.GetY ());
                buff.Write ((int) actor.Dir);
                buff.Write (actor.Status);
                conn[i].End (buff);
            }
        }

        public void CreateWoodWall(int tid, int i, int j) {

            string tag = "wall";
            if(i >= 23 && i <= 25 && j >= 11 && j <= 14) {
                tag = "basewall";
            }

            float x = 0, y = 0;
            CoordinateUtil.IntToFloatX (i, out x);
            CoordinateUtil.IntToFloatY (j, out y);

            Actor[] wood = new Actor[4];    // 一个木墙需要分成四个小木墙
            wood[0] = CreateActor (tid, x + 0.125f, y - 0.125f, 0.25f, tag);
            wood[1] = CreateActor (tid, x + 0.125f, y + 0.125f, 0.25f, tag);
            wood[2] = CreateActor (tid, x - 0.125f, y - 0.125f, 0.25f, tag);
            wood[3] = CreateActor (tid, x - 0.125f, y + 0.125f, 0.25f, tag);

            wood[0].UDActor = wood[1];
            wood[0].LRActor = wood[2];

            wood[1].UDActor = wood[0];
            wood[1].LRActor = wood[3];

            wood[2].UDActor = wood[3];
            wood[2].LRActor = wood[0];

            wood[3].UDActor = wood[2];
            wood[3].LRActor = wood[1];

        }

        #endregion

        public void OnTimeElapsed(int te) {

            foreach(Actor a in locomotorObjects) {
                Repos (a);
            }

        }

        public void NeedLocomotor(Actor actor) {
            locomotorObjects.AddLast (actor);
        }

        public void Close() {
            Alive = false;
            foreach(Actor a in objects) {
                a.Alive = false;
            }
            locomotorObjects.Clear ();
            objects.Clear ();

            for(int i = 0; i < ICoroutineList.Count; i++) {
                if(ICoroutineList[i].Finished == false) {
                    CM.Stop (ICoroutineList[i]);
                }
            }
        }

        #region 提供控制功能



        public void PauseAllEnemy() {
            foreach(Actor a in objects) {
                if(a.Tag == "enemy") {
                    a.Paused = true;
                }
            }
        }

        public void KillAllByTag(string tag) {
            List<Actor> removed = new List<Actor> ();

            foreach(Actor a in objects) {
                if(a.Tag.Equals(tag)) {
                    removed.Add (a);
                }
            }

            for(int i = 0; i < removed.Count; i++) {
                RemoveActor (removed[i]);
            }
        }

        #endregion

        #region 提供碰撞检查的功能

        /// <summary>
        /// 判断 a 是否遇到障碍，如果遇到障碍，进行回调，回调返回 true，调整 a 的位置为当前方向最接近障碍的地方。
        /// </summary>
        /// <param name="a"></param>
        /// <param name="callback"></param>
        /// <returns></returns>
        public bool IsBlock(Actor a, Func<Actor, bool> callback) {

            if(a.GetX () < CoordinateUtil.MinX + a.Size/2) {  // 上下边界
                a.SetX (CoordinateUtil.MinX + a.Size / 2);
                return true;
            } else if(a.GetX () > CoordinateUtil.MaxX - a.Size/2) {
                a.SetX (CoordinateUtil.MaxX - a.Size / 2);
                return true;
            }

            if(a.GetY () < CoordinateUtil.MinY + a.Size/2) {  // 左右边界
                a.SetY (CoordinateUtil.MinY + a.Size / 2);
                return true;
            } else if(a.GetY () > CoordinateUtil.MaxY - a.Size/2) {
                a.SetY (CoordinateUtil.MaxY - a.Size / 2);
                return true;
            }

            foreach(Actor o in objects) {
                if(a == o || a == o.Owner || o.Owner == a || a.Owner != null && o.Owner != null && a.Owner.Tag.Equals (o.Owner.Tag)) {
                    // a 、o 是同一物件，或 a 、o 有关系
                    continue;
                }

                if(o.Tag == "grass") {       // 草地对坦克来说不是障碍
                    continue;
                }
              
                if(IsOverlapped (a, o)) {    // 有重叠

                    if(!callback (o)) {      // 有重叠需要判断的不一定都是障碍，有可能是食物
                        continue;
                    }

                    if(a.Dir == DIR.UP) {
                        a.SetX (o.GetSX () - a.Size + a.Size / 2);
                    } else if(a.Dir == DIR.DOWN) {
                        a.SetX (o.GetSX () + o.Size + a.Size / 2);
                    } else if(a.Dir == DIR.LEFT) {
                        a.SetY (o.GetSY () + o.Size + a.Size / 2);
                    } else {
                        a.SetY (o.GetSY () - a.Size + a.Size / 2);
                    }

                    return true;
                }
            }

            return false;
        }

        /// <summary>
        /// 检查actor 是否发生碰撞，并且返回所有障碍，用于子弹
        /// </summary>
        /// <param name="a"></param>
        /// <param name="obstacles"></param>
        /// <returns></returns>
        public bool IsBlock(Actor actor, out List<Actor> obstacles) {
            List<Actor> res = new List<Actor> ();
            obstacles = res;

            if (actor.GetX() < CoordinateUtil.MinX + actor.Size / 2) {  // 上下边界
                return true;
            }
            else if (actor.GetX() > CoordinateUtil.MaxX - actor.Size / 2) {
                return true;
            }

            if (actor.GetY() < CoordinateUtil.MinY + actor.Size / 2) {  // 左右边界
                return true;
            }
            else if (actor.GetY() > CoordinateUtil.MaxY - actor.Size / 2) {
                return true;
            }

            foreach (Actor a in objects) {

                if(a == actor || a == actor.Owner || a.Owner == actor || a.Owner!=null && actor.Owner!=null && a.Owner.Tag.Equals(actor.Owner.Tag)) {
                    continue;
                }

                if(a.Tag == "grass")
                    continue;
                if(a.Tag == "water")
                    continue;
                if(a.Tag == "slide")
                    continue;
                if(a.Tag == "food")
                    continue;

                if(IsOverlapped (actor, a)) {
                    res.Add (a);
                }
            }

            if(obstacles.Count > 0) {
                return true;
            } else {
                return false;
            }
        }

        /// <summary>
        /// 判断 c、d 是否重叠，通过 b 的四个角是否在 a 所占据的矩形内判断，（b是c、d中较小者，a是c、d中较大者）
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        public bool IsOverlapped(Actor c, Actor d) {

            Actor a, b;
            if(c.Size > d.Size) {
                a = c;
                b = d;
            } else {
                a = d;
                b = c;
            }

            if(CoordinateUtil.IsInSquare (a.GetSX (), a.GetSY (), a.Size, b.GetSX () + CoordinateUtil.EPS, b.GetSY () + CoordinateUtil.EPS))
                return true;
            else if(CoordinateUtil.IsInSquare (a.GetSX (), a.GetSY (), a.Size, b.GetSX () + b.Size - CoordinateUtil.EPS, b.GetSY () + CoordinateUtil.EPS))
                return true;
            else if(CoordinateUtil.IsInSquare (a.GetSX (), a.GetSY (), a.Size, b.GetSX () + CoordinateUtil.EPS, b.GetSY () + b.Size - CoordinateUtil.EPS))
                return true;
            else if(CoordinateUtil.IsInSquare (a.GetSX (), a.GetSY (), a.Size, b.GetSX () + b.Size - CoordinateUtil.EPS, b.GetSY () + b.Size - CoordinateUtil.EPS))
                return true;

            return false;
        }

        #endregion

        public void Add(ICoroutine i) {
            ICoroutineList.Add (i);
        }

        #region 保护部分

        List<ICoroutine> ICoroutineList = new List<ICoroutine> ();

        /// <summary>
        /// 工厂是否还在工作
        /// </summary>
        public bool Alive {
            get;
            protected set;
        }

        /// <summary>
        /// 连接的两个客户端
        /// </summary>
        protected Connection[] conn;

        /// <summary>
        /// 存放所有创建出来的Actor，可用于判断是否碰撞
        /// </summary>
        protected LinkedList<Actor> objects = new LinkedList<Actor> ();

        /// <summary>
        /// 存放所有需要实时刷新位置的物体
        /// </summary>
        protected LinkedList<Actor> locomotorObjects = new LinkedList<Actor> ();

        protected int kill = 0;

        #endregion

        public int GetKill() {
            return kill;
        }
    }
}
